//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_HDD_FILE_H
#define _UXS_HDD_FILE_H

#pragma once

#include "BaseFile.h"

namespace UX_STL
{
namespace File
{
	using namespace _hidden_;


	//
	// HDD Read only File
	//

	class HddRFile : public RFile
	{
	// types
	public:
		SHARED_POINTER( HddRFile );


	// variables
	protected:
		Ptr< FILE >		_file;
		string			_name;


	// methods
	public:
		HddRFile ()		{}
		~HddRFile ()	{ _Close(); }


		static HddRFilePtr New ()
		{
			return new HddRFile();
		}

		static HddRFilePtr New (uni_c_string address)
		{
			HddRFilePtr	file = new HddRFile();

			if ( file->Open( address ) )
				return file;

			return null;
		}


		bool Open (uni_c_string address)
		{
			Close();
			_name = address;
			_file = fopen( address.cstr(), "rb" );
			return IsOpened();
		}


		// RFile //
		virtual isize ReadBuf (void * buf, usize size) override
		{
			return fread( buf, 1, size, _file );
		}


		// BaseFile //
		virtual void Close () override
		{
			return _Close();
		}

		virtual bool IsOpened () override
		{
			return _file.IsNotNull();
		}

		virtual bool SeekSet (usize offset) override
		{
			return fseek( _file, (long)offset, SEEK_SET ) == 0;
		}

		virtual bool SeekCur (isize offset) override
		{
			return fseek( _file, (long)offset, SEEK_CUR ) == 0;
		}

		virtual bool SeekEnd (usize offset) override
		{
			return fseek( _file, (long)offset, SEEK_END ) == 0;
		}
		
		virtual usize RemainingSize () const override
		{
			usize size = 0;
			usize pos  = ftell( _file );

			fseek( _file, 0, SEEK_END );
			size = ftell( _file );
			fseek( _file, (long)pos, SEEK_SET );

			return size - pos;
		}

		virtual usize Size () const override
		{
			usize size = 0;
			usize pos  = ftell( _file );

			fseek( _file, 0, SEEK_END );
			size = ftell( _file );
			fseek( _file, (long)pos, SEEK_SET );

			return size;
		}

		virtual usize Pos () const override
		{
			return ftell( _file );
		}
		
		virtual usize IsEOF () const override
		{
			return Pos() >= Size();
		}

		virtual uni_c_string	Name () const override
		{
			return _name;
		}

		virtual EFile::type		GetType () const override
		{
			return EFile::HDD;
		}


	private:
		void _Close ()
		{
			if ( IsOpened() )
			{
				fclose( _file );
				_file = null;
			}
			_name.Clear();
		}
	};



	//
	// HDD Write only File
	//

	class HddWFile : public WFile
	{
	// types
	public:
		enum EOpenFlags
		{
			NONE	= 0,
			APPEND	= 1 << 0,
			UPDATE	= 1 << 1,
		};

		SHARED_POINTER( HddWFile );


	// variables
	protected:
		Ptr< FILE >		_file;
		string			_name;


	// methods
	public:
		HddWFile ()		{}
		~HddWFile ()	{ _Close(); }
		

		static HddWFilePtr New ()
		{
			return new HddWFile();
		}

		static HddWFilePtr New (uni_c_string address, int flags = NONE)
		{
			HddWFilePtr	file = new HddWFile();

			if ( file->Open( address, flags ) )
				return file;

			return null;
		}


		bool Open (uni_c_string address, int flags = NONE)
		{
			char	mode[8]	= {0};
			int		pos		= 0;

			if ( EnumCmp( flags, APPEND ) )
				mode[ pos++ ] = 'a';
			else
				mode[ pos++ ] = 'w';

			mode[ pos++ ] = 'b';

			if ( EnumCmp( flags, UPDATE ) )
				mode[ pos++ ] = '+';

			mode[ pos++ ] = 0;


			Close();
			_name = address;
			_file = fopen( address.cstr(), mode );
			return IsOpened();
		}


		// WFile //
		virtual isize WriteBuf (const void * buf, usize size) override
		{
			return fwrite( buf, 1, size, _file );
		}
		
		virtual void Flush () override
		{
			fflush( _file );
		}


		// BaseFile //
		virtual void Close () override
		{
			return _Close();
		}

		virtual bool IsOpened () override
		{
			return _file.IsNotNull();
		}

		virtual bool SeekSet (usize offset) override
		{
			return fseek( _file, (long)offset, SEEK_SET ) == 0;
		}

		virtual bool SeekCur (isize offset) override
		{
			return fseek( _file, (long)offset, SEEK_CUR ) == 0;
		}

		virtual bool SeekEnd (usize offset) override
		{
			return fseek( _file, (long)offset, SEEK_END ) == 0;
		}
		
		virtual usize RemainingSize () const override
		{
			usize size = 0;
			usize pos  = ftell( _file );

			fseek( _file, 0, SEEK_END );
			size = ftell( _file );
			fseek( _file, (long)pos, SEEK_SET );

			return size - pos;
		}

		virtual usize Size () const override
		{
			usize size = 0;
			usize pos  = ftell( _file );

			fseek( _file, 0, SEEK_END );
			size = ftell( _file );
			fseek( _file, (long)pos, SEEK_SET );

			return size;
		}

		virtual usize Pos () const override
		{
			return ftell( _file );
		}
		
		virtual usize IsEOF () const override
		{
			return Pos() >= Size();
		}

		virtual uni_c_string	Name () const override
		{
			return _name;
		}

		virtual EFile::type		GetType () const override
		{
			return EFile::HDD;
		}


	private:
		void _Close ()
		{
			if ( IsOpened() )
			{
				fclose( _file );
				_file = null;
			}
			_name.Clear();
		}
	};

}	// File


namespace UXTypes
{
	typedef SHARED_POINTER_TYPE( File::HddRFile )	HddRFilePtr;
	typedef SHARED_POINTER_TYPE( File::HddWFile )	HddWFilePtr;

}	// UXTypes
}	// UX_STL

#endif	// _UXS_HDD_FILE_H